package com.autumn.platform.core;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.cglib.core.ReflectUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.NumberUtils;

import com.autumn.platform.core.logger.Logs;
import com.autumn.platform.core.spring.SpringHolder;

public class Defaults {

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Lookup {

    }

    public interface LookupFor {
        Set<Class<?>> getClasses();
    }

    private static final Properties properties = new Properties();

    private static final Map<String, Object> valueMap = new ConcurrentHashMap<String, Object>();

    private static final Map<String, Object> componentMap = new ConcurrentHashMap<String, Object>();

    static {
        loadDefaults();
        loadMergeDefaults();
    }

    /**
     * 注册默认值
     * 
     * @param cls
     * @param value
     */
    public static <E> void registerDefaultValue(String name, E value) {
        valueMap.put(name, value);
    }

    /**
     * 注册默认组件
     * 
     * @param cls
     * @param component
     */
    public static <E> void registerDefaultComponent(Class<E> cls, E component) {
        componentMap.put(cls.getName(), component);
    }

    /**
     * 注册默认组件
     * 
     * @param cls
     * @param name
     * @param component
     */
    public static <E> void registerDefaultComponent(Class<E> cls, String name, E component) {
        componentMap.put(getComponentName(cls, name), component);
    }

    /**
     * 获取默认设置
     * 
     * @param name 配置名称
     * @param cls 目标类型，一般为八种基本类型和字符串
     * @return 目标类型的设置值
     */
    public static <E> E getDefaultValue(String name, Class<E> cls) {
        return getDefaultInner(valueMap, name, cls);
    }

    /**
     * 如果传入值为null，返回默认配置名称相对应的值
     * 
     * @param value 传入值
     * @param defaultConfigName 默认配置名称
     * @return 配置值
     */
    public static <T> T getDefaultValueIfNull(T value, String name, Class<T> cls) {
        if (null == value) {
            return getDefaultValue(name, cls);
        }
        return value;
    }

    /**
     * 获取默认设置组
     * 
     * @param name 配置名称
     * @param cls 目标类型，一般为八种基本类型和字符串
     * @return name对应的多个值，使用逗号分隔配置多个值
     */
    public static <E> List<E> getDefaultValues(String name, Class<E> cls) {
        return getDefaultInners(valueMap, name, cls);
    }

    /**
     * 如果值传入集合为null或者空，返回默认配置名称相对应的集合
     * 
     * @param values 传入集合
     * @param name 默认配置名称
     * @return 配置值
     */
    public static <T> List<T> getDefaultValuesIfEmpty(List<T> values, String name, Class<T> cls) {
        if (null == values || values.isEmpty()) {
            return getDefaultValues(name, cls);
        }
        return values;
    }

    /**
     * 合并集合，如果传入集合为空，返回默认配置名称相对应的值组，否则在传入集合基础上添加默认的配置
     * 
     * @param values 用户配置值
     * @param name 默认配置名称
     * @return 配置值
     */
    public static <T> List<T> mergeDefaultValues(List<T> values, String name, Class<T> cls) {
        List<T> s = getDefaultValues(name, cls);
        if (null != s && s.size() >= 1) {
            if (null == values || values.isEmpty()) {
                return s;
            } else {
                values.addAll(s);
            }
        }
        return values;
    }

    /**
     * 获取默认组件，将组件类型的类名称作为key值从属性文件中获取相应配置的实现类，然后实例化并返回
     * 
     * @param cls 组件类型，一般为接口
     * @return 配置的组件实现类
     */
    public static <T> T getDefaultComponent(Class<T> cls) {
        return getDefaultInner(componentMap, cls.getName(), cls);
    }

    /**
     * 如果传入组件为null，将组件类型作为key值查找对应的默认组件
     * 
     * @param component 用户配置组件
     * @param cls 组件类型
     * @return 配置组件
     */
    public static <E> E getDefaultComponentIfNull(E component, Class<E> cls) {
        if (null == component) {
            return getDefaultComponent(cls);
        }
        return component;
    }

    /**
     * 获取默认组件，将组件类型的类名称加井号#，再加上name作为key值从属性文件中获取相应配置的实现类，然后实例化并返回
     * 
     * @param cls 组件类型，一般为接口
     * @param name 名称
     * @return 配置的组件实现类组
     */
    public static <T> T getDefaultComponent(Class<T> cls, String name) {
        return getDefaultInner(componentMap, getComponentName(cls, name), cls);
    }

    /**
     * 如果传入组件为空，将组件类型加上井号#，再加上名称作为key值查找对应的默认组件
     * 
     * @param component 用户配置组件
     * @param cls 组件类型
     * @param name 组件名称
     * @return 配置组件
     */
    public static <E> E getDefaultComponentIfNull(E component, Class<E> cls, String name) {
        if (null == component) {
            return getDefaultComponent(cls, name);
        }
        return component;
    }

    /**
     * 获取默认组件集合，将组件类型的类名称作为key值从属性文件中获取相应配置的实现类，然后实例化并返回
     * 
     * @param cls 组件类型，一般为接口
     * @return 配置的组件实现类组，使用逗号分隔配置多个值
     */
    public static <T> List<T> getDefaultComponents(Class<T> cls) {
        return getDefaultInners(componentMap, cls.getName(), cls);
    }

    /**
     * 如果传入集合为空，将组件类型作为key值查找对应的默认组件组
     * 
     * @param components 用户配置组件组
     * @param cls 组件类型
     * @return 配置组件组
     */
    public static <E> List<E> getDefaultComponentsIfEmpty(List<E> components, Class<E> cls) {
        if (null == components || components.isEmpty()) {
            return getDefaultComponents(cls);
        }
        return components;
    }

    /**
     * 合并组件集合，如果传入集合为空，返回组件类型作为key值对应的默认组件集合，否则在原基础上添加默认的配置组件
     * 
     * @param components 用户配置组件组
     * @param cls 组件类型
     * @return 配置组件组
     */
    public static <E> List<E> mergeDefaultComponents(List<E> components, Class<E> cls) {
        List<E> d = getDefaultComponents(cls);
        if (null != d && d.size() > 0) {
            if (null == components || components.isEmpty()) {
                return d;
            } else {
                components.addAll(d);
            }
        }
        return components;
    }

    private static String getComponentName(Class<?> cls, String name) {
        return cls.getName() + "#" + name;
    }

    @SuppressWarnings({"unchecked"})
    private static <T> T getDefaultInner(Map<String, Object> map, String name, Class<T> cls) {
        if (!map.containsKey(name)) {
            synchronized (map) {
                if (!map.containsKey(name)) {
                    String vp = properties.getProperty(name);
                    T rs = convertValue(cls, vp);
                    properties.remove(name);
                    if (null != rs) {
                        map.put(name, rs);
                    }
                }
            }
        }
        return (T) map.get(name);
    }

    @SuppressWarnings("unchecked")
    private static <T> List<T> getDefaultInners(Map<String, Object> map, String name, Class<T> cls) {
        if (!map.containsKey(name)) {
            synchronized (map) {
                if (!map.containsKey(name)) {
                    List<T> rs = null;
                    String vp = properties.getProperty(name);
                    if (Utils.isBlank(vp)) {
                        rs = null;
                        Logs.debug("the default value of [" + name + "] is null");
                    } else if ("[]".equals(vp)) {
                        rs = new ArrayList<T>();
                    } else {
                        String[] names = vp.split("\\s*,\\s*");
                        rs = new ArrayList<T>(names.length);
                        for (int i = 0, l = names.length; i < l; i++) {
                            rs.add(convertValue(cls, names[i]));
                        }
                    }
                    properties.remove(name);
                    if (null != rs) {
                        map.put(name, rs);
                    }
                }
            }
        }
        return (List<T>) map.get(name);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private static <T> T convertValue(Class<T> cls, String vp) {
        T rs = null;
        if (null == vp) {
            return null;
        } else if (Utils.isBlank(vp)) {
            if (cls.equals(String.class)) {
                rs = cls.cast("");
            } else {
                rs = null;
            }
        } else if (String.class.equals(cls)) {
            rs = cls.cast(vp);
        } else if (boolean.class.equals(cls) || Boolean.class.equals(cls)) {
            Boolean b = Utils.string2Boolean(vp);
            return (T) b;
        } else if (cls.isPrimitive()) {
            rs = SpringHolder.convert(vp, cls);
        } else if (Number.class.isAssignableFrom(cls)) {
            rs = cls.cast(NumberUtils.parseNumber(vp, (Class<? extends Number>) cls));
        } else if (Enum.class.isAssignableFrom(cls)) {
            Class enumClass = cls;
            rs = (T) Enum.valueOf(enumClass, vp);
        } else {
            try {
                rs = cls.cast(ReflectUtils.newInstance(Class.forName(vp)));
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return rs;
    }

    /**
     * 加载默认配置
     */
    private synchronized static void loadDefaults() {
        try {
            /**
             * 加载平台包下面的defaults.properties文件
             */
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath*:META-INF/autumn-defaults*.properties");
            if (null != resources) {
                List<Properties> list = new ArrayList<Properties>();
                for (Resource resource : resources) {
                    InputStream input = null;
                    try {
                        Properties properties = new Properties();
                        input = resource.getInputStream();
                        properties.load(input);
                        list.add(properties);
                    } catch (Exception e) {
                        // ignore
                    } finally {
                        Utils.closeQuietly(input);
                    }
                }
                /**
                 * 根据配置文件中的order排序
                 */
                Collections.sort(list, new Comparator<Properties>() {
                    @Override
                    public int compare(Properties o1, Properties o2) {
                        return Integer.parseInt(o2.getProperty("order", "0")) - Integer.parseInt(o1.getProperty("order", "0"));
                    }
                });
                for (Properties p : list) {
                    p.remove("order");
                    properties.putAll(p);
                }
            }
        } catch (IOException ignore) {
        }
    }

    /**
     * 加载需要将多个文件组合在一起的默认配置
     */
    private synchronized static void loadMergeDefaults() {
        try {
            /**
             * 加载平台包下面的mergeDefaults.properties文件
             */
            Map<String, Set<String>> combines = new HashMap<String, Set<String>>();
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath*:META-INF/autumn-merge-defaults*.properties");
            if (null != resources) {
                for (Resource resource : resources) {
                    InputStream input = null;
                    try {
                        Properties properties = new Properties();
                        input = resource.getInputStream();
                        properties.load(input);
                        for (String key : properties.stringPropertyNames()) {
                            String value = properties.getProperty(key);
                            if (!Utils.isBlank(value)) {
                                String[] values = value.split("\\s*,\\s*");
                                for (String v : values) {
                                    if (!Utils.isBlank(v)) {
                                        Set<String> l = combines.get(key);
                                        if (null == l) {
                                            l = new LinkedHashSet<String>();
                                            combines.put(key, l);
                                        }
                                        l.add(v);
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        // ignore
                    } finally {
                        Utils.closeQuietly(input);
                    }
                }
                for (String key : combines.keySet()) {
                    Set<String> l = combines.get(key);
                    if (null != l && !l.isEmpty()) {
                        StringBuffer sb = new StringBuffer();
                        for (String s : l) {
                            sb.append(",").append(s);
                        }
                        properties.put(key, sb.substring(1));
                    }
                }
            }
        } catch (IOException ignore) {
        }
    }
}
